#include <stdio.h>
#include <stdint.h>
#include <string.h>

typedef unsigned char u_char;
struct mtd_info { int dummy; };

#define NAND_OOB_SIZE		64
#define NAND_PAGE_SIZE		2048
#define NAND_RAW_PAGE_SIZE	(NAND_PAGE_SIZE + NAND_OOB_SIZE)

static const u_char nand_ecc_precalc_table[] = {
	0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
	0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
	0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
	0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
	0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
	0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
	0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
	0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
	0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
	0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
	0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
	0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
	0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
	0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
	0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
	0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
};

/**
 * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256-byte block
 * @mtd:	MTD block structure
 * @dat:	raw data
 * @ecc_code:	buffer for ECC
 */
int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
		       u_char *ecc_code)
{
	uint8_t idx, reg1, reg2, reg3, tmp1, tmp2;
	int i;

	/* Initialize variables */
	reg1 = reg2 = reg3 = 0;

	/* Build up column parity */
	for(i = 0; i < 256; i++) {
		/* Get CP0 - CP5 from table */
		idx = nand_ecc_precalc_table[*dat++];
		reg1 ^= (idx & 0x3f);

		/* All bit XOR = 1 ? */
		if (idx & 0x40) {
			reg3 ^= (uint8_t) i;
			reg2 ^= ~((uint8_t) i);
		}
	}

	/* Create non-inverted ECC code from line parity */
	tmp1  = (reg3 & 0x80) >> 0; /* B7 -> B7 */
	tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */
	tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */
	tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */
	tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */
	tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */
	tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */
	tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */

	tmp2  = (reg3 & 0x08) << 4; /* B3 -> B7 */
	tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */
	tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */
	tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */
	tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */
	tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */
	tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */
	tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */

	/* Calculate final ECC code */
#ifdef CONFIG_MTD_NAND_ECC_SMC
	ecc_code[0] = ~tmp2;
	ecc_code[1] = ~tmp1;
#else
	ecc_code[0] = ~tmp1;
	ecc_code[1] = ~tmp2;
#endif
	ecc_code[2] = ((~reg1) << 2) | 0x03;

	return 0;
}

static void nand_calculate_page_ecc(struct mtd_info *mtd, const u_char *page,
			u_char *ecc_code)
{
	int i = 0, ecc_idx = 0;

	/* 256B -> 3B ECC, 2K -> 24B ECC */
	for (ecc_idx = 0; ecc_idx < NAND_OOB_SIZE - 24; ecc_idx++)
		ecc_code[ecc_idx] = 0xFF;

	for (i = 0; i < NAND_PAGE_SIZE; i += 256, ecc_idx += 3)
		nand_calculate_ecc(NULL, &page[i], &ecc_code[ecc_idx]);
}

static void dump_bad_page(const u_char *raw_page, u_char *calc_ecc)
{
	int i = 0;
	const u_char *data = &raw_page[0];
	const u_char *ecc  = &raw_page[NAND_PAGE_SIZE];

	for (i = 0; i < NAND_PAGE_SIZE; i++) {
		if (i % 16 == 0)
			printf("%03x:  ", i);

		printf("%02x ", data[i]);

		if ((i + 1) % 16 == 0) {
			printf("\n");
		} else if ((i + 1) % 8 == 0) {
			printf("  ");
		}
	}

	printf("      OOB:\n");

	for (i = 0; i < NAND_OOB_SIZE; i += 8) {
		printf("%03x:  ", i);

		printf("%02x %02x %02x %02x %02x %02x %02x %02x | ",
			ecc[0], ecc[1], ecc[2], ecc[3],
			ecc[4], ecc[5], ecc[6], ecc[7]);

		printf("%02x %02x %02x %02x %02x %02x %02x %02x \n",
			calc_ecc[0], calc_ecc[1], calc_ecc[2], calc_ecc[3],
			calc_ecc[4], calc_ecc[5], calc_ecc[6], calc_ecc[7]);

		ecc += 8;
		calc_ecc += 8;
	}

	printf("\n");
}

static int check_and_dump_bad(const u_char *raw_page, int size,
				const char *prompt)
{
	int i = 0;
	char calc_ecc[64];
	const char *data, *ecc;

	for (i = 0; i < size; i += NAND_RAW_PAGE_SIZE) {
		data = &raw_page[0];
		ecc  = &raw_page[NAND_PAGE_SIZE];

		nand_calculate_page_ecc(NULL, data, calc_ecc);
		if (memcmp(ecc, calc_ecc, sizeof(calc_ecc))) {
			printf("%s", prompt);
			dump_bad_page(data, calc_ecc);
		}
	}

	return 0;
}

int main(int argc, char *argv[])
{
	FILE *fp;
	int i = 0;

	char prompt[1024];
	unsigned long offt = 0;
	u_char raw_page[NAND_RAW_PAGE_SIZE];

	if (argc < 2) {
		fprintf(stderr, "Usage: %s xxx.bin ...\n",
				argv[0]);
		return -1;
	}

	for (i = 1; i < argc; i++) {
		if (!(fp = fopen(argv[1], "rb"))) {
			fprintf(stderr, "can't open binary file: %s\n",
				argv[1]);
			continue;
		}

		for(offt = 0; ; offt += sizeof(raw_page)) {
			snprintf(prompt, sizeof(prompt), "%s   offset 0x%08lx\n",
					argv[i], offt);

			if (sizeof(raw_page) != fread(
					raw_page, 1, sizeof(raw_page), fp)) {
				break;
			}

			check_and_dump_bad(raw_page, sizeof(raw_page), prompt);
		}

		fclose(fp);
	}

	return 0;
}

